﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Bitirme.DAL.Entities;
using Bitirme.DAL.Enum;

namespace Bitirme.Controllers
{
    public class Scheduler
    {
        Project project;
        ProjectHandler projectHandler;
        EmployeeHandler employeeHandler;
        AssignmentHandler assignmentHandler;
        List<Employee> employeeList;
        String result = String.Empty;
        List<Assignment> assignmentList;

        public Scheduler(int projectID)
        {
            projectHandler = new ProjectHandler();
            employeeHandler = new EmployeeHandler();
            assignmentHandler = new AssignmentHandler();
            project = projectHandler.GetProjectsWithAllAttributesByID(projectID);
            employeeList = employeeHandler.GetAllEmployeesWithSkills();
            assignmentList = new List<Assignment>();
        }

        /*** (1) Parametre olarak verilen workpackages'in requirement(capability ve cost) 
         * koşulunu sağlayacak elemanları liste olarak döndürür.
         * 
         * TODO: Şu an sadece capability karşılaştırılıyor, cost da dahil edilebilir
         * */
        protected List<Employee> findProperEmployeesForWorkPackage(WorkPackage package)
        {
            List<Employee> employees = new List<Employee>();
            foreach (Employee employee in employeeList)
            {
                foreach (KeyValuePair<Capability, SkillLevel> capability in employee.Skills)
                {
                    if ((package.Requirement.RequirementCapability.Name).ToLower() == (capability.Key.Name).ToLower())
                    {
                        employees.Add(employee);
                        break;
                    }
                }
            }
            return employees;
        }

        public List<ScheduledWorkPackages> createWorkPackageQuery(List<WorkPackage> workPackages)
        {

            ScheduledWorkPackages scheduledWorkPackage;
            List<ScheduledWorkPackages> tempQuery = new List<ScheduledWorkPackages>();
            List<ScheduledWorkPackages> scheduleWorkPackageQuery = new List<ScheduledWorkPackages>();
            foreach (WorkPackage package in workPackages)
            {

                scheduledWorkPackage = new ScheduledWorkPackages(package);
                scheduledWorkPackage.ProperEmployees = findProperEmployeesForWorkPackage(package);
                scheduledWorkPackage.InitialStartDate = project.StartDate;
                scheduledWorkPackage.Assignment = assignmentHandler.GetAssignmentsByWorkPackageID(package.ID);
                tempQuery.Add(scheduledWorkPackage);
                if (scheduledWorkPackage.Assignment.Count > 0)
                    scheduledWorkPackage.isAlreadyAssigned = true;
                foreach (Assignment assigned in scheduledWorkPackage.Assignment)
                {
                    //assigned start date daha erken bir tarihte ise
                    if (DateTime.Compare(scheduledWorkPackage.InitialStartDate, assigned.StartDate) > 0)
                        scheduledWorkPackage.InitialStartDate = assigned.StartDate;
                    //assigned end date daha geç bir tarih ise 
                    if (DateTime.Compare(scheduledWorkPackage.InitialEndDate, assigned.EndDate) < 0)
                        scheduledWorkPackage.InitialEndDate = assigned.EndDate;

                    //TODO: assignedin bitiş tarihi çok geç ise bu eleman gereksiz onu hesapla
                }
            }

            var sortedQuery = from package in tempQuery
                              orderby package.ProperEmployees.Count
                              select package;

            //Siralı halde scheduledworkpackageslar geliyor..
            foreach (ScheduledWorkPackages package in sortedQuery)
            {
                //scheduled workpackageslar sıralı hale getiriliyor..
                scheduleWorkPackageQuery.Add(package);
            }

            //siralı haldeki scheduledworkpackageslerden
            foreach (ScheduledWorkPackages sortedScheduled in scheduleWorkPackageQuery)
            {
                //sirali haldeki scheduledworkpackagesler içinden
                foreach (ScheduledWorkPackages sPackage in scheduleWorkPackageQuery)
                {
                    //sırasız haldeki dependencyleri getiriliyor..
                    foreach (WorkPackage dependency in sortedScheduled.Dependency)
                    {
                        if (dependency.ID == sPackage.ID)
                        {
                            sortedScheduled.ScheduledDepedency.Add(sPackage);
                            break;
                        }
                    }
                }
            }

            return scheduleWorkPackageQuery;
        }

        public List<Assignment> naber()
        {

            List<ScheduledWorkPackages> scheduleQuery = createWorkPackageQuery(project.PackageList);
            foreach (ScheduledWorkPackages package in scheduleQuery)
            {
                if (package.isScheduled)
                    continue;

                if (package.Dependency.Count > 0)
                {
                    foreach (ScheduledWorkPackages parent in package.ScheduledDepedency)
                    {

                        DateTime date = findStartDateInDependent(parent);
                        if (DateTime.Compare(package.InitialStartDate, date) < 0) // degistii
                        {
                            package.InitialStartDate = date.AddDays(1);
                        }
                    }
                    schedule(package);

                }
                else if (package.Dependency.Count == 0)
                {
                    schedule(package);
                }
            }

            return assignmentList;
        }

        public bool schedule(ScheduledWorkPackages package)
        {

            System.Diagnostics.Debug.WriteLine("ID: " + package.ID + "INITDate: " + package.InitialStartDate + "ENDDate: " + package.InitialEndDate);

            if (package.isScheduled) // Schedule edilen bir daha edilmesin diye.
                return false;
            if (package.ProperEmployees.Count == 0)
            {
                result = "HATA!!," + package.Name + " icin uygun bir atama yapılamıyor...";
                return false;
            }



            float totalHourPerDay = 0;

            foreach (Employee employee in package.ProperEmployees)
            {
                bool startDateChanged = false;
                Assignment tempAssignment = new Assignment();
                tempAssignment.StartDate = package.InitialStartDate;
                // TODO: Temp assignmentlar bir listede tutulup kontrol edilmeli !!!!

                tempAssignment.StartDate = package.InitialStartDate; //Proper employee için uygun bir assignment hazırlanır
                if (tempAssignment.StartDate != project.StartDate)
                {
                    startDateChanged = true;
                }
                // Employee'nin bu proje için daha önceden yapılan atamaları incelenir
                foreach (Assignment assigned in assignmentList)
                {
                    if (employee.ID == assigned.Employee.ID)
                    {
                        //Employee'ye atanan işin bitiş tarihi bu projenin başlangıç tarihinden geç ise 
                        if (DateTime.Compare(tempAssignment.StartDate, assigned.EndDate) < 0)
                            tempAssignment.StartDate = assigned.EndDate.AddDays(1);

                        {
                            tempAssignment.StartDate = assigned.EndDate.AddDays(1); //Bu assignmentin başlangıç tarihi atanan işin bitiş tarihine kaydırılır
                            startDateChanged = true;
                        }
                        /*** TODO: Employee'ye atanan işin başlangıç tarihine göre de bir karşılaştırma yapılamlı ***/
                    }

                }
                //Başlangıç tarihi belirlenen assignmentin, employee tam çalıştığında olacak bitiş tarihi hesaplanır
                if (tempAssignment.StartDate.Day == 1)
                    tempAssignment.EndDate = tempAssignment.StartDate.AddDays(Math.Ceiling(package.Requirement.ManHour / employee.HourPerDay) - 1);
                else
                    tempAssignment.EndDate = tempAssignment.StartDate.AddDays(Math.Ceiling(package.Requirement.ManHour / employee.HourPerDay));



                // databasedeki assignmentları kontrol eder
                List<Assignment> assignmentToEmployee;
                assignmentToEmployee = assignmentHandler.GetAssignmentsByEmployeeID(employee.ID);
                foreach (Assignment assigned in assignmentToEmployee)
                {
                    //assigned end date daha geç bir tarihte ise
                    if (DateTime.Compare(tempAssignment.StartDate, assigned.EndDate) < 0)
                        tempAssignment.StartDate = assigned.EndDate.AddDays(1);


                    // Atanmış işin başlangıcı, atanacak işin başlangıcından önce veya aynı ise
                    if (DateTime.Compare(assigned.StartDate, tempAssignment.StartDate) <= 0)
                    {
                        //Atanacak işin başlanıgıcını, atanmış işin bitişine taşı
                        if (!startDateChanged)
                            tempAssignment.StartDate = assigned.EndDate.AddDays(1);
                        //Atanacak işin bitişini tam çalışma olduğunda hesapla
                        if (tempAssignment.StartDate.Day == 1)
                            tempAssignment.EndDate = tempAssignment.StartDate.AddDays(Math.Ceiling(package.Requirement.ManHour / employee.HourPerDay) - 1);
                        else
                            tempAssignment.EndDate = tempAssignment.StartDate.AddDays(Math.Ceiling(package.Requirement.ManHour / employee.HourPerDay));

                        //TODO: assignedin bitiş tarihi çok geç ise bu eleman gereksiz onu hesapla
                    }
                    // Atanmış işin başlangıcı, atanacak işin başlangıcından sonra ise
                    else
                    {
                        //TODO: burada bişiler olmalı!!
                        tempAssignment.EndDate = assigned.StartDate.AddDays(-1);
                    }
                }
                //=====================================================================================



                if (package.InitialStartDate.Day == 1)
                    tempAssignment.EndDate = tempAssignment.StartDate.AddDays(Math.Ceiling(package.Requirement.ManHour / employee.HourPerDay) - 1);
                else
                    tempAssignment.EndDate = tempAssignment.StartDate.AddDays(Math.Ceiling(package.Requirement.ManHour / employee.HourPerDay));


                //totalHourPerDay += employee.HourPerDay;

                tempAssignment.HourPerDay = employee.HourPerDay;
                tempAssignment.WorkPackage = package;
                tempAssignment.Employee = employee;
                assignmentList.Add(tempAssignment);

            }


            var sortedassign = from assign in assignmentList
                               orderby assign.StartDate
                               select assign;

            foreach (Assignment assign in sortedassign)
            {

                if (assign.WorkPackage.ID == package.ID)
                {

                    package.Assignment.Add(assign);


                }
            }

            package.arrangeAssignments();
            package.isScheduled = true;
            return true;

        }

        public DateTime findStartDateInDependent(ScheduledWorkPackages parent)
        {

            if (parent.Dependency.Count == 0)
            {
                schedule(parent);
                return parent.InitialEndDate;
            }
            else
            {
                foreach (ScheduledWorkPackages grandParent in parent.ScheduledDepedency)
                {

                    DateTime date = findStartDateInDependent(grandParent);
                    if (DateTime.Compare(parent.InitialStartDate, date) < 0)
                    {
                        parent.InitialStartDate = date.AddDays(1);
                    }
                }
                schedule(parent);
                return parent.InitialEndDate;
            }
        }
    }
}